using IQIGame.Onigao.Framework;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using UnityEngine;

namespace IQIGame.Onigao.Game
{
    /// <summary>
    /// 网络协议管理类
    /// </summary>
    public class NetManager : Singleton<NetManager>
    {
        private struct Section : IComparable<Section>
        {
            public int MinId;
            public int MaxId;

            public int CompareTo(Section o)
            {
                if (MinId > o.MaxId)
                    return 1;
                else if (o.MinId <= MinId && MinId <= o.MaxId)
                    return 0;
                else
                    return -1;
            }

        }


        /// <summary>
        /// msgId映射表，不同的channel处理的msgId不一样
        /// key=msgId
        /// value=channelName
        /// </summary>
        private readonly SortedDictionary<Section, string> m_msgIdChannelNameMap = new SortedDictionary<Section, string>();
        /// <summary>
        /// 单次网络连接
        /// key=channelName
        /// value=session
        /// </summary>
        private readonly ConcurrentDictionary<string, NetSession> m_sessions = new ConcurrentDictionary<string, NetSession>();

        private NetManager()
        {
            FrameworkEntry.Network.NetworkClosedEventHandler += OnServerConnectionClosed;
            FrameworkEntry.Network.NetworkErrorEventHandler += OnNetWorkError;
        }

        /// <summary>
        /// 创建channel
        /// </summary>
        public void CreateNetworkChannel(string channelName, int heartBeatInterval, int minMsgID, int maxMsgID)
        {
            Section section;
            section.MaxId = maxMsgID;
            section.MinId = minMsgID;
            if (m_msgIdChannelNameMap.ContainsKey(section))
            {
                m_msgIdChannelNameMap[section] = channelName;
            }
            else
            {
                m_msgIdChannelNameMap.Add(section, channelName);
            }
            INetworkChannel channel = FrameworkEntry.Network.GetNetworkChannel(channelName);
            if (channel == null)
            {
                channel = FrameworkEntry.Network.CreateNetworkChannel(channelName, NetworkChannelHelper.Instance);
                channel.HeartBeatInterval = heartBeatInterval;

                channel.RegisterHandler(HandlePacket);
            }
        }

        /// <summary>
        /// 服务器发来的消息包处理
        /// </summary>
        /// <param name="basePacket"></param>
        /// <param name="channel"></param>
        private void HandlePacket(BasePacket basePacket, INetworkChannel channel)
        {
            ReceivePackage package = (ReceivePackage)basePacket;
            NetSession session = GetSession(channel.Name);
            session.ProcessReceivePackage(package);
            package.Active = false;
            ConcurrentClassPool<ReceivePackage>.Put(package);
        }

        /// <summary>
        /// 底层心跳驱动
        /// </summary>
        internal void SendHeartBeat(INetworkChannel channel)
        {
            if (m_sessions.TryGetValue(channel.Name, out var session))
            {
                session.SendHeartBeat();
            }
        }

        ///// <summary>
        ///// 通过消息id获取对应的channelName
        ///// </summary>
        ///// <param name="msgId"></param>
        ///// <returns></returns>
        //private string GetHandleChannelName(int msgId)
        //{
        //    Section section;
        //    section.MaxId = section.MinId = msgId;
        //    m_msgIdChannelNameMap.TryGetValue(section, out string channelName);
        //    return channelName;
        //}

        ///// <summary>
        ///// 发送消息
        ///// </summary>
        ///// <param name="pack"></param>
        ///// <returns></returns>
        //public bool SendPack(SendPackage pack)
        //{
        //    string channelName = GetHandleChannelName(pack.Header.Messagid);
        //    if (channelName == null)
        //    {
        //        return false;
        //    }
        //    NetSession session = GetSession(channelName);
        //    if (session == null)
        //    {
        //        return false;
        //    }
        //    return session.SendPack(pack);
        //}

        /// <summary>
        /// 获取频道消息id范围
        /// </summary>
        /// <param name="channelName"></param>
        /// <returns></returns>
        public (int, int) GetChannelMsgIdRange(string channelName)
        {
            foreach (var kv in m_msgIdChannelNameMap)
            {
                if (kv.Value.Equals(channelName))
                {
                    return (kv.Key.MinId, kv.Key.MaxId);
                }
            }
            return (0, 0);
        }

        /// <summary>
        /// 连接某一个频道
        /// </summary>
        /// <param name="channelName"></param>
        /// <param name="cNetHandler"></param>
        /// <param name="sessionConnector"></param>
        /// <param name="host"></param>
        /// <param name="port"></param>
        public async Task<NetSession> ConnectToChannel(string channelName, CNetHandler cNetHandler, INetSessionConnector sessionConnector, string host, int port)
        {
            //m_sessions.TryAdd之前tryRemove一次, 以防万一, 正常逻辑不会有oldSession
            if (m_sessions.TryRemove(channelName, out NetSession oldSession))
            {
                oldSession.Close(true);
                oldSession.Dispose();
            }
            INetworkChannel channel = ManagerCenter.Network.GetNetworkChannel(channelName);
            //必须在ConnectToChannel之前添加, 不然可能连接上马上就收消息的时候找不到session
            NetSession session = new NetSession(channel, cNetHandler, sessionConnector, host, port);
            m_sessions.TryAdd(channelName, session);

            //开始连接网络
            bool connected = await ConnectToChannel(channel, host, port);
            if (!connected)
            {
                //连接失败需要移除session
                if (m_sessions.TryRemove(channelName, out NetSession removeSession))
                {
                    removeSession.Close(true);
                    removeSession.Dispose();
                }
                return null;
            }
            return session;
        }

        private IPAddress GetServerIpAddress(string ip)
        {
            IPAddress[] addressArray = Dns.GetHostAddresses(ip);
            IPAddress address = null;
            if (addressArray != null && addressArray.Length > 0)
            {
                foreach (var addr in addressArray)
                {
                    //优先使用ipv4
                    if (addr.AddressFamily == AddressFamily.InterNetwork)
                    {
                        return addr;
                    }
                }
                //没有ipv4就用第一个
                address = addressArray[0];
            }
            else
            {
                address = IPAddress.Parse(ip);
            }
            return address;
        }


        internal Task<bool> ConnectToChannel(INetworkChannel channel, string host, int port)
        {
            if (string.IsNullOrEmpty(host))
            {
                Debug.LogError("host string is invalid.");
                return Task.FromResult(false);
            }
            IPAddress ipAddress = GetServerIpAddress(host);
            Debug.LogFormat($"start connect addressList {ipAddress}:{port}");
            return channel.Connect(ipAddress, port, null);
        }

        /// <summary>
        /// 获取session
        /// </summary>
        /// <param name="channelName"></param>
        /// <returns></returns>
        public NetSession GetSession(string channelName)
        {
            m_sessions.TryGetValue(channelName, out NetSession session);
            return session;
        }

        /// <summary>
        /// 获取所有session
        /// </summary>
        /// <returns></returns>
        public List<NetSession> GetAllSession()
        {
            return new List<NetSession>(m_sessions.Values);
        }

        /// <summary>
        /// 关闭所有session
        /// </summary>
        public void CloseAllSession()
        {
            foreach (var kv in m_sessions)
            {
                kv.Value.Close(true);
            }
        }

        /// <summary>
        /// 销毁所有session
        /// </summary>
        public void DestoryAllSession()
        {
            foreach (var kv in m_sessions)
            {
                kv.Value.Close(true);
                kv.Value.Dispose();
            }
            m_sessions.Clear();
        }

        /// <summary>
        /// 销毁Session
        /// </summary>
        /// <param name="channelName"></param>
        public void DestroySession(string channelName)
        {
            if (m_sessions.TryRemove(channelName, out NetSession session))
            {
                session.Close(true);
                session.Dispose();
            }
        }

        /// <summary>
        /// 网络错误
        /// </summary>
        private void OnNetWorkError(INetworkChannel channel, NetworkErrorCode errorCode, SocketError socketError, string errorMessage, object userData)
        {
            ThreadHelper.Instance.CallInMainThread(() =>
            {
                Debug.LogWarningFormat($"network channel = {channel.Name}: Network Error, msg = {errorMessage}");
                channel.Close();
            });
        }

        /// <summary>
        /// 连接断开
        /// </summary>
        /// <param name="channel"></param>
        private void OnServerConnectionClosed(INetworkChannel channel)
        {
            ThreadHelper.Instance.CallInMainThread(async () =>
            {
                NetSession session = GetSession(channel.Name);
                if (session == null)
                {
                    //还未连接上网络,就断开连接
                    Debug.LogWarningFormat($"OnServerConnectionClosed session is null, channel = {channel.Name}");
                    return;
                }
                Debug.LogWarningFormat($"OnServerConnectionClosed channel = {channel.Name}");
                if (session.IsClosed)
                {
                    Debug.LogWarningFormat($"session closed, channel = {session.ChannelName}");
                    return;
                }
                else if (session.WaitProtocolConnectResult)
                {
                    //在连接协议的过程中被断开了连接
                    Debug.LogWarningFormat($"channel be closed in WaitProtocolConnectResult, channel = {session.ChannelName}");
                    session.Close();
                    return;
                }
                else if (session.IsReconnecting)
                {
                    //在重新连接网络的过程中被断开了连接
                    Debug.LogWarningFormat($"channel be closed in Connecting, channel = {session.ChannelName}");
                    //session.Close();
                    return;
                }
                else
                {
                    for (int i = 0; i < 3; i++)
                    {
                        if (!session.CanReconnect())
                        {
                            break;
                        }
                        bool netResult = await session.Reconnect();
                        if (netResult)
                        {
                            //网络连接成功就直接return了, 后续由协议重连逻辑接管
                            return;
                        }
                    }
                    Debug.LogWarningFormat($"can not autoReconnect to channel = {session.ChannelName}");
                    await Task.Delay(100);//让close事件延后到error事件之后
                    //网络一直没有连接成功, 网络断开, 可以尝试手动重连
                    session.SessionConnector.OnSessionDisconnected(session);
                    return;
                }
            });
        }

    }
}